<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    /*
    * 尾递归：
    *   递归的升级版：
    *
    * 普通递归存在的问题：看下面的求阶乘的递归函数存在的问题
    *   调用栈会被保留，作用域也会被保留，会导致内存泄露
    *
    *   解决办法：直接return递归函数执行 想法设法不要写出return一个表达式的情况 回到求阶乘中就是想办法把return n*foo(n-1)中的n*去掉
    *       像下面的foo1函数一样，内部函数执行的返回结果，和外部函数返回的结果是同一个东西（函数），在这种情况下，浏览器会对这个函数进行优化
    *
    *   核心就是利用参数储存结果，迭代结果
    *
    * */

    function foo(n){
        if(n === 1) return 1 //结束条件，结束n等于1的最基本情况
        return n * foo(n-1) //求n的阶乘 return的是一个表达式 return真正执行的时候 是右边的表达式计算出结果的时候 才能被return执行返回出来
        //而表达式又会从一个大函数再分解成一个小函数，一直循环分解下去直到找到结束条件
        //所以在表达式没有计算出结果的时候，会有一大堆内存被占用，作用域一直没有消失，一直在累积，如果计算的量够大，会导致最大调用栈溢出
    }

    //假设我们去掉了n*的函数是foo1
    function foo1(n){ //外部函数
        if(n === 1) return 1
        return foo1(n-1) //内部函数
    }
    //上面这个foo1函数中，外部函数的返回值是内部函数，外部函数foo1接收的形参是n，运行后返回内部函数foo1，内部函数foo1接收的形参是(n-1)，
    //两者是同一个函数在自己调用自己，自己返回自己，只是每次传入的参数不一样

    //利用参数存储结果，将最上面的foo函数改写成以下形式
    function foo2(n,s = 1){
        if(n === 1) return s
        return foo2(n-1,s * n)
    }

    /*上面的函数可以在下列例子中进行验证
    * foo2(4,s=1){ //第一次执行，形参n=4，形参s=1
    *   if(n === 1) return s
    *   return foo2(3,1 * 4) //执行之后，形参n的值为(n-1)=4-1=3，形参s的值为(s*n)=1*4
    * }
    * foo2(3,1 * 4){ //第二次执行，形参n的值为第一次返回的参数值(n-1)=3，即n=(n-1)=3，形参s的值变成了第一次返回的参数值(s*n)=1*4，即s=1*4
    *   if(n === 1) return s
    *   return foo2(2,1 * 4 * 3) //执行之后，形参n的值为(n-1)=(3-1)=2,形参s的值为(s*n)=(1*4)*3
    * }
    * foo2(2,1 * 4 * 3){ //第三次执行，形参n的值为第二次返回的参数值(n-1)=(3-1)=2，形参s的值为第二次返回的参数值(s*n)=(1*4)*3
    *   if(n === 1) return s
    *   return foo2(1,1 * 4 * 3 * 2) //执行之后，形参n的值为(n-1)=(2-1)=1，形参s的值为(s*n)=((1*4)*3)*2
    * }
    * foo2(1,1 * 4 * 3 * 2){ //第四次执行，形参n的值为第三次返回的参数值(n-1)=(2-1)=1，形参s的值为第三次返回的参数值(s*n)=((1*4)*3)*2
    *   if(n === 1) return (1 * 4 * 3 * 2) //结束条件是当n的值等于1时，直接返回s的值，即return (((1*4)*3)*2) -> 1*2*3*4
    * }
    *
    *
    * */

    //斐波拉契 1 1 2 3 5 8
    function fb(n,start = 1,end = 1){
        if(n === 1) return start
        if(n === 2) return end
        return fb(n-1,end,start+end)
    }

    /*
    * fb(5,start = 1,end = 1){
    *   if(n === 1) return start
    *   if(n === 2) return end
    *   return fb(4,1,2)
    * }
    *
    * fb(4,1,2){
    *   if(n === 1) return start
    *   if(n === 2) return end
    *   return fb(3,2,3)
    * }
    *
    * fb(3,2,3){
    *   if(n === 1) return start
    *   if(n === 2) return end
    *   return fb(2,3,5)
    * }
    *
    * fb(2,3,5){
    *   if(n === 1) return start
    *   if(n === 2) return 5
    * }
    *
    * */


</script>
</body>
</html>